package maintain

import ( "github.com/garyburd/redigo/redis"
//	"fmt" 
	"testing"
	"time"
	"log"
	"strconv"
	"strings"
	"errors"
	"net" )

func ResetRedisData(redis_address string, t *testing.T) {
	c, err := redis.DialTimeout("tcp", redis_address, 0, 1*time.Second, 1*time.Second)
	if err != nil { t.Fatal(err) }
	_, err = c.Do("DEL", "rlist", "slist", "wdlist", "rslist", "room:1", "room:2", "room:3", "room:4", "srv:1", "srv:2", "srv:3", "srv:4", "wd:1", "wd:2", "srvIdAlloc")
	if err != nil { t.Fatal(err) }
//	_, err = c.Do("SAVE")
//	if err != nil { t.Fatal(err) }
}

func InitWatchDogRedisData(redis_address string, t *testing.T) {
	c, err := redis.DialTimeout("tcp", redis_address, 0, 1*time.Second, 1*time.Second)
	now := time.Now().Unix()
	_, err = c.Do("SADD", "wdlist", "1", "2")
	if err != nil { t.Fatal(err) }
	_, err = c.Do("HMSET", "wd:1", "status", "offline", "load", "1.0", "ttl", "5", "hb", now, "inip", "127.0.0.1", "exip", "192.168.20.214", "rsrv", "2")
	if err != nil { t.Fatal(err) }
	_, err = c.Do("HMSET", "wd:2", "status", "offline", "load", "1.0", "ttl", "5", "hb", now, "inip", "192.168.20.215", "exip", "192.168.20.215")
	if err != nil { t.Fatal(err) }
//	_, err = c.Do("SAVE")
//	if err != nil { t.Fatal(err) }
}

func InitServerRedisData(redis_address string, t *testing.T) {
	c, err := redis.DialTimeout("tcp", redis_address, 0, 1*time.Second, 1*time.Second)
	now := time.Now().Unix()
	_, err = c.Do("SET", "srvIdAlloc", "4")
	if err != nil { t.Fatal(err) }
	_, err = c.Do("SADD", "slist", "1", "2", "3", "4")
	if err != nil { t.Fatal(err) }
	_, err = c.Do("HMSET", "srv:1", "exAddr", "192.168.20.212:7657", "inAddr", "192.168.20.212:7658", "status", "ready", "load", "0.1", "wdId", "1", "ttl", "5", "hb", now, "pid", "12345")
	if err != nil { t.Fatal(err) }
	_, err = c.Do("HMSET", "srv:2", "exAddr", "192.168.20.213:7657", "inAddr", "192.168.20.213:7658", "status", "ready", "load", "0.1", "wdId", "1", "ttl", "5", "hb", now, "pid", "12346")
	if err != nil { t.Fatal(err) }
	_, err = c.Do("HMSET", "srv:3", "exAddr", "192.168.20.214:7657", "inAddr", "192.168.20.214:7658", "status", "ready", "load", "0.1", "wdId", "2", "ttl", "5", "hb", now, "pid", "12345")
	if err != nil { t.Fatal(err) }
	_, err = c.Do("HMSET", "srv:4", "exAddr", "192.168.20.215:7657", "inAddr", "192.168.20.215:7658", "status", "reserve", "load", "0.0", "wdId", "2", "ttl", "5", "hb", now, "pid", "12345")
	if err != nil { t.Fatal(err) }
	_, err = c.Do("RPUSH", "rslist", "4")
	if err != nil { t.Fatal(err) }
//	_, err = c.Do("SAVE")
//	if err != nil { t.Fatal(err) }
}

func InitRoomRedisData(redis_address string, t *testing.T) {
	c, err := redis.DialTimeout("tcp", redis_address, 0, 1*time.Second, 1*time.Second)
	now := time.Now().Unix()
	_, err = c.Do("SADD", "rlist", "1", "2", "3")
	if err != nil { t.Fatal(err) }
	_, err = c.Do("HMSET", "room:1", "type", "1", "startAt", now, "srvId", "1")
	if err != nil { t.Fatal(err) }
	_, err = c.Do("HMSET", "room:2", "type", "1", "startAt", now, "srvId", "2")
	if err != nil { t.Fatal(err) }
	_, err = c.Do("HMSET", "room:3", "type", "1", "startAt", now, "srvId", "3")
	if err != nil { t.Fatal(err) }
//	_, err = c.Do("HMSET", "room:4", "type", "1", "startAt", 0, "srvId", "0")
//	if err != nil { t.Fatal(err) }
//	_, err = c.Do("HMSET", "room:5", "type", "1", "startAt", 0, "srvId", "0")
//	if err != nil { t.Fatal(err) }
//	_, err = c.Do("HMSET", "room:6", "type", "1", "startAt", 0, "srvId", "0")
//	if err != nil { t.Fatal(err) }
//	_, err = c.Do("SAVE")
//	if err != nil { t.Fatal(err) }
}

func Conn(serverAddress string) (data *RedisData, err error) {
	var srvAddr *net.TCPAddr
	srvAddr, err = net.ResolveTCPAddr("tcp4", serverAddress)
	if err!= nil {
		err = errors.New("Conn() failed! error:" + err.Error()); 
		return 
	}
	return ConnByAddress(srvAddr)
}

func ConnByAddress(srvAddr *net.TCPAddr) (data *RedisData, err error) {
	data = new(RedisData)
	data.ServerAddress = srvAddr
	data.TcpConn, err = net.DialTCP("tcp", nil, srvAddr)
	if err != nil { log.Println("ConnByAddress() failed"); return }
	laddr := strings.Split(data.TcpConn.LocalAddr().String(), ":")
	if laddr == nil || len(laddr) != 2 {
		defer data.Close(); 
		err = errors.New("Can not get local IP from : " + data.TcpConn.LocalAddr().String())
		return 
	}
	data.LocalIp = laddr[0]

	conn := redis.NewConn(data.TcpConn, 5 * time.Second, 5 * time.Second)
	data.Conn = &conn

	err = data.UpdateTimestamp()
	if err != nil {
		defer data.Close()
		return
	}

	return
}

func (data *RedisData) Close() {
	if data.Conn != nil {
		(*data.Conn).Close()
	}
}

func (data *RedisData) Reconn() (err error) {
	data.Close()
	
	var newData *RedisData
	newData, err = ConnByAddress(data.ServerAddress)
	if err != nil {	log.Println("RedisData::Reconn() failed"); return }
	data.Copy(newData)
	return
}


func (redisData *RedisData) UpdateTimestamp() (err error) {
	var values []interface{}
	values, err = redis.Values((*redisData.Conn).Do("TIME"))
	if err != nil {
		return
	}
	if len(values) < 1 {
		err = errors.New("UpdateTimestamp() can not parse the time")
		return
	}
	var timeInRedis int64
	var secString string
	secString, err = redis.String(values[0], err)
	if err != nil {
		log.Println("UpdateTimestamp() parse reply failed! reply: ", values[0])
		return
	}

	timeInRedis, err = strconv.ParseInt(secString, 10, 32)
	if err != nil {
		log.Println("UpdateTimestamp() parse reply failed! secString: ", secString)
		return
	}
	now := time.Now().Unix()
	redisData.TimeDiff = timeInRedis - now
	return
}

func (redisData *RedisData) UpdateMaps(maps *Maps) (err error) {
	err = redisData.UpdateWatchDogMap(maps)
	if err != nil { return }
	err = redisData.UpdateServerMap(maps)
	if err != nil { return }
	err = redisData.UpdateRoomMap(maps)
	return
}

func (redisData *RedisData) UpdateWatchDogMap(maps *Maps) (err error) {
	if redisData == nil || redisData.Conn == nil || maps == nil {
		err = errors.New("UpdateWatchDogMap() Param error!, redis or redis.Conn or maps is nil")
		return 
	}

	// Get watch dog map
	var list []interface{}
	list, err = redis.Values((*redisData.Conn).Do("SORT", "wdlist",
					"GET", "#",
					"GET", "wd:*->status",
					"GET", "wd:*->load",
					"GET", "wd:*->ttl",
					"GET", "wd:*->hb",
					"GET", "wd:*->rsrv",
					"GET", "wd:*->inip",
					"GET", "wd:*->exip"))
	if err != nil {
		return
	}
	newMap := make(WatchDogMap)
	for i := 0; len(list) > 0; i++ {
		var id, ttl, hb, rsrv int
		var load float32
		var status, inip, exip string
		list, err = redis.Scan(list, &id, &status, &load, &ttl, &hb, &rsrv, &inip, &exip)
		if err == nil {
			newWd := new(WatchDog)
			newWd.Id = id
			newWd.Status = status
			newWd.Load = load
			newWd.Ttl = ttl
			newWd.ReserveServerNumber = rsrv
			newWd.HeartBeat = hb
			newWd.ExIp = exip
			newWd.InIp = inip
			newMap[id] = newWd
		} else {
			return
		}
	}
	maps.WatchDogMap = &newMap
	return 
}

func (redisData *RedisData) UpdateServerMap(maps *Maps) (err error) {
	if redisData == nil || redisData.Conn == nil || maps == nil {
		err = errors.New("UpdateServerMap() Param error!, redis or redis.Conn or maps is nil")
		return 
	}

	// Get server map
	var list []interface{}
	list, err = redis.Values((*redisData.Conn).Do("SORT", "slist",
						"GET", "#",
						"GET", "srv:*->exAddr",
						"GET", "srv:*->inAddr",
						"GET", "srv:*->status",
						"GET", "srv:*->load",
						"GET", "srv:*->wdId",
						"GET", "srv:*->pid",
						"GET", "srv:*->ttl",
						"GET", "srv:*->hb"))
	if err != nil {
		return
	}
	serverMap := make(ServerMap)
	for i := 0; len(list) > 0; i++ {
		var id, wdId, ttl, hb, pid int
		var exAddr, inAddr, status string
		var load float32
		list, err = redis.Scan(list, &id, &exAddr, &inAddr, &status, &load, &wdId, &pid, &ttl, &hb)
		if err == nil {
			wdmap := maps.WatchDogMap
			if wd, found := (*wdmap)[wdId]; found {
				newSrv := new(Server)
				newSrv.Id = id
				newSrv.ExAddr = exAddr
				newSrv.InAddr = inAddr
				newSrv.Status = status
				newSrv.Load = load
				newSrv.Ttl = ttl
				newSrv.Pid = pid
				newSrv.HeartBeat = hb
				newSrv.WatchDog = wd
				serverMap[id] = newSrv
				wd.Servers = append(wd.Servers, newSrv)
			} else {
				err = errors.New("UpdateServerMap() failed!, no watch dog id: " + strconv.Itoa(wdId))
				return
			}
		} else {
			return
		}
	}
	maps.ServerMap = &serverMap
	return
}

func (redisData *RedisData) UpdateRoomMap(maps *Maps) (err error) {
	if redisData == nil || redisData.Conn == nil || maps == nil {
		err = errors.New("UpdateRoomMap() Param error!, redis or redis.Conn or maps is nil")
		return 
	}

	// Get room list
	var list []interface{}
	list, err = redis.Values((*redisData.Conn).Do("SORT", "rlist", 
						"GET", "#",
						"GET", "room:*->type",
						"GET", "room:*->startAt",
						"GET", "room:*->srvId"))
	if err != nil {
		return
	}
	roomMap := make(RoomMap)
	for i := 0; len(list) > 0; i++ {
		var id, typ, startAt, srvId int
		list, err = redis.Scan(list, &id, &typ, &startAt, &srvId)
		if err == nil {
			serverMap := maps.ServerMap
			if srv, found := (*serverMap)[srvId]; found {
				newRoom := new(Room)
				newRoom.Id = id
				newRoom.Type = typ
				newRoom.StartAt = startAt
				newRoom.Server = srv
				srv.Rooms = append(srv.Rooms, newRoom)
				roomMap[id] = newRoom
			} else {
				log.Println("Room[", id, "]'s serverId(", srvId, ") not existed in server map")
			}
		} else {
			log.Println("Scan room error:", err)
		}
	}
	maps.RoomMap = &roomMap
	return
}
